/**头文件区 */
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>              // 以I2C方式连接LCD
#include <LiquidCrystal_I2C.h> //引用LCD的I2C库
#include <ArduinoJson.h>       // 使用json发送数据
#include <SoftwareSerial.h>
#include <AliyunIoTSDK.h>//引入阿里云 IoT SDK

/* 连接您的WIFI SSID和密码 */
#define WIFI_SSID "esp8266test"
#define WIFI_PASSWD "qwertyui"

/* 设备的三元组信息*/
#define PRODUCT_KEY "gdb9d8QpRRe"
#define DEVICE_NAME "test02"
#define DEVICE_SECRET "ead60e25a653b904735d29f2cb736d10"
#define REGION_ID "cn-shanghai"

/* 线上环境域名和端口号，不需要改 */
#define MQTT_SERVER PRODUCT_KEY ".iot-as-mqtt." REGION_ID ".aliyuncs.com"
#define MQTT_PORT 1883
#define MQTT_USRNAME DEVICE_NAME "&" PRODUCT_KEY
#define CLIENT_ID "test02|securemode=3,signmethod=hmacsha1,timestamp=1234567890|"
// 算法工具: http://iot-face.oss-cn-shanghai.aliyuncs.com/tools.htm 进行加密生成password
// password教程 https://www.yuque.com/cloud-dev/iot-tech/mebm5g
#define MQTT_PASSWD "c0f03065a5d9b0a8d5fdb35ab1ca214e5b723d0f"

#define ALINK_BODY_FORMAT "{\"id\":\"test02\",\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":%s}"
#define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"

//const char* in_Topic_name="***";//所接收主题的名称


/* 设备声明*/
// wifi和服务器
WiFiClient espClient;
PubSubClient client(espClient);

/**设置LCD1602设备，这里的地址是0X27，十六位，两行：
 * 会占用D3、D4：
 * GND 连接nodemcu的GND，
 * VCC 连接nodemcu的VCC，
 * SDA 连接nodemcu的 D4 引脚 。
 * SCL 连接 nodemcu的D3 引脚 。 
 */
LiquidCrystal_I2C lcd(0x27, 16, 2);
SoftwareSerial toArduinoA(14, 12); //RX=d5,TX=d6，软串口

/* 变量初始化*/
// 特殊全局变量
unsigned long lastMs = 0; // 用来实现每隔x秒发送一次
unsigned char count = 0;  // 计数器
// int displayStatus = 0;    // lcd显示状态
String S_val = ""; //存储串口接收的字符串
// 传感器
/* 环境大类*/
bool autoMode; // FullAutoModeSwitch: 全自动开关，由用户设置，不发送
bool autoMode_prev;
int illumination, illumination_sum; // 传感器读取的环境亮度，是状态量
float RH, T, RH_sum, T_sum;         // 温湿度模块，状态量
int brightness;                     // Brightness: 设备亮度,自动模式下由传感器决定，否则由用户设置，值由最后时刻值决定
int fan;                            // FanState: 风扇。同设备亮度
bool curtain;                       // CurtainOperation: 窗帘状态，同设备亮度
int brightness_prev, fan_prev;      //上一次的状态量
bool curtain_prev;                  //上一次的状态量
/* 防盗大类*/
bool antiTheft; //AntiTheft: 防盗是否开启，由用户设置，不发送
bool antiTheft_prev;
bool motion, collide, tilt;                             // MotionAlarmState红外、CollideStatus碰撞、OverTiltError倾斜；
bool alarmState;                                        // AlarmState警报状态，当前面几个传感器状态不都为0且antiTheft打开时为1，发送,不过这个可以放到主体那里
bool motion_sum, collide_sum, tilt_sum, alarmState_sum; // 用来检测是否出现过1
//----------------------------------------------------------------

/* wifi初始化*/
void wifiInit()
{
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWD); //连接WiFi
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WiFi not Connect");
    }
    Serial.println("Connected to AP");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("espClient [");
    client.setServer(MQTT_SERVER, MQTT_PORT); /* 连接WiFi之后，连接MQTT服务器 */
    client.setCallback(callback);
}

/* mqtt检查与初始提示*/
void mqttCheckConnect()
{
    while (!client.connected())
    {
        Serial.println("Connecting to MQTT Server ...");
        if (client.connect(CLIENT_ID, MQTT_USRNAME, MQTT_PASSWD))

        {
            //client.subscribe("in_Topic_name");//订阅主题
            Serial.println("MQTT Connected!");
        }
        else
        {
            Serial.print("MQTT Connect err:");
            Serial.println(client.state());
            delay(5000);
        }
    }
}

// mqtt发送信息
void mqttIntervalPost()
{
    char param[64];
    char jsonBuf[256]; //拼接

    //以下代码重复使用
    // ----------------------------------------------------------------
    //发送温度，需要平均
    sprintf(param, "{\"CurrentTemperature\":%f}", T_sum / count); //{"CurrentTemperature":25.0}
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    boolean d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish Temperature success");
    }
    else
    {
        Serial.println("publish Temperature fail");
    }
    //湿度，需要平均
    //https://www.cnblogs.com/ZhengBlogs/p/add.html
    sprintf(param, "{\"CurrentHumidity\":%f}", RH_sum / count); //{"CurrentHumidity":25.0}
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish Humidity success");
    }
    else
    {
        Serial.println("publish Humidity fail");
    }
    //环境亮度，需要平均
    sprintf(param, "{\"Illumination\":%d}", illumination_sum / count);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish Illumination success");
    }
    else
    {
        Serial.println("publish Illumination fail");
    }
    // 设备亮度
    sprintf(param, "{\"Brightness\":%d}", brightness);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish Brightness success");
    }
    else
    {
        Serial.println("publish Brightness fail");
    }
    // 窗帘状态
    sprintf(param, "{\"CurtainOperation\":%d}", curtain);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish CurtainOperation success");
    }
    else
    {
        Serial.println("publish CurtainOperation fail");
    }
    // 风扇状态
    sprintf(param, "{\"FanState\":%d}", fan);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("FanState success");
    }
    else
    {
        Serial.println("publish FanState fail");
    }
    // 红外状态
    sprintf(param, "{\"MotionAlarmState\":%d}", motion_sum); 
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("MotionAlarmState success");
    }
    else
    {
        Serial.println("publish MotionAlarmState fail");
    }
    //碰撞状态
    sprintf(param, "{\"CollideStatus\":%d}", collide_sum);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish CollideStatus success");
    }
    else
    {
        Serial.println("publish CollideStatus fail");
    }
    //倾斜状态
    sprintf(param, "{\"OverTiltError\":%d}", tilt_sum);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish OverTiltError success");
    }
    else
    {
        Serial.println("publish OverTiltError fail");
    }
    //AlarmState警报状态
    sprintf(param, "{\"AlarmState\":%d}", alarmState_sum);
    sprintf(jsonBuf, ALINK_BODY_FORMAT, param);
    Serial.println(jsonBuf);
    d = client.publish(ALINK_TOPIC_PROP_POST, jsonBuf);
    if (d)
    {
        Serial.println("publish AlarmState success");
    }
    else
    {
        Serial.println("publish AlarmState fail");
    }
}

//https://www.cnblogs.com/ZhengBlogs
void setup()
{
    Wire.begin(2, 0);
    lcd.init();
    lcd.backlight();
    /* initialize serial for debugging */
    Serial.begin(115200);
    Serial.println("Demo Start");
    wifiInit();
    //留地方初始化
}

//反控，等待修改
void callback(char *topic, byte *payload, unsigned int length)
{
    Serial.print("Message arrived in topic: ");
    Serial.println(topic);
    Serial.print("Message:");
    String message;
    for (int i = 0; i < length; i++)
    {
        message = message + (char)payload[i]; // convert *byte to string
    }
    Serial.print(message);

   
    Serial.println();
    Serial.println("-----------------------");
    // 如果json
    // autoMode = doc["FullAutoModeSwitch"];
    // brightness = doc["Brightness"];
    // curtain = doc["CurtainOperation"];
    // fan = doc["FanState"];
    // antiTheft = doc["AntiTheft"];
    // 如果只是几个数字
    // if (message.length() > 0)
    // {
    //     int pos = 0;
    //     int spilt = 0;
    //     String str[8];
    //     int num[8];
    //     for (int i = 0; i < message.length(); i++)
    //     {
    //         if (comdata[i] != ',') //逗号为分隔符
    //         {
    //             str[pos] += comdata[i];
    //         }
    //         else
    //         {
    //             pos++;
    //         }
    //     }
    //     for (int i = 0; i < 8; i++)
    //     {
    //         num[i] = str[i].toInt();//转为整数型
    //         //Serial.println(str[i]);
    //         //Serial.println(num[i]);
    //     }
    // }
    // message = "";
    // 好像也可以直接传递给另一台arduino，或者在这里封包成json，都简单，再讨论
}

void display()
{
    //默认：lcd 显示温度
    lcd.setCursor(0, 0); //设置显示指针
    lcd.print("Have a nice day!");
    lcd.setCursor(0, 1);
    lcd.print("T");
    lcd.print(T);
    lcd.print("*C");
    lcd.print(" H");
    lcd.print(RH);
    lcd.print("%");
    delay(500);
    if (antiTheft != antiTheft_prev) // 当切换防盗
    {
        if (antiTheft == 1)
        {
            lcd.setCursor(0, 1);
            lcd.print("Anti-theft on   ");
            delay(800);
        }
        else
        {
            lcd.setCursor(0, 1);
            lcd.print("Anti-theft off   ");
            delay(800);
        }
        antiTheft_prev = antiTheft;
    }
    if (alarmState == 1)
    {
        lcd.setCursor(0, 0); //设置显示指针
        lcd.print("!!!!!!!!!!!!!!!!");
        lcd.setCursor(0, 1); //设置显示指针
        lcd.print("Someone in!!!!!!!");
        delay(800);
    }

    if (autoMode != autoMode_prev) // 当切换自动
    {
        if (autoMode == 1)
        {
            lcd.setCursor(0, 1);
            lcd.print("Anti-theft on   ");
            delay(800);
        }
        else
        {
            lcd.setCursor(0, 1);
            lcd.print("Anti-theft off   ");
            delay(800);
        }
        autoMode_prev = autoMode;
    }

    if (autoMode == 0) //当没开启自动模式，改变数值会自动显示
    {
        {
            if (brightness != brightness_prev)
            {
                lcd.setCursor(0, 0);
                lcd.print("The brightness     ");
                lcd.setCursor(0, 1);
                lcd.print("of lamp is ");
                lcd.print(brightness);
                lcd.print("   ");
                brightness_prev = brightness;
            }
            else if (curtain != curtain_prev)
            {
                lcd.setCursor(0, 0);
                lcd.print("The status of   ");
                lcd.setCursor(0, 1);
                if (fan == 0)
                {
                    lcd.print("  curtain: OFF  ");
                }
                else
                {
                    lcd.print("  curtain: OPEN ");
                }
                curtain_prev = curtain;
            }
            else if (fan != fan_prev)
            {
                lcd.setCursor(0, 0);
                lcd.print("The speed of   ");
                lcd.setCursor(0, 1);
                lcd.print("fan: ");
                lcd.print(fan);
                fan_prev = fan;
            }
            delay(800);
        }
    }
}
// the loop function runs over and over again forever
void loop()
{
    /*接受其他arduino传来的数据 */
    String arduinoA_string = "";
    //while (toArduinoA.available() > 0)
    while (toArduinoA.available() >= 0)
    {
        if (toArduinoA.peek() != '\n')
        {
            arduinoA_string += char(toArduinoA.read());
            delay(5);
        }

        //模拟从其它设备读取完毕后的值
        RH = 50;
        T = 25;
        illumination = 200;
        brightness = 100;
        autoMode = 0;
        fan = 50;
        curtain = 1;

        antiTheft = 0;
        motion = 0;
        collide = 0;
        tilt = 0;
        alarmState = 0;

        //解码
        DynamicJsonDocument doc(256); //存储收到的
        deserializeJson(doc, arduinoA_string);
        RH = doc["CurrentHumidity"];
        T = doc["CurrentTemperature"];
        illumination = doc["Illumination"];
        motion = doc["MotionAlarmState"];
        collide = doc["CollideStatus"];
        tilt = doc["OverTiltError"];
        //显示
        display();
        // 需要求和的完成求和或赋值操作
        RH_sum += RH;
        T_sum += T;
        illumination_sum += illumination;
        motion_sum += motion;
        collide_sum += collide;
        tilt_sum += tilt;
        alarmState_sum += alarmState;
        brightness_prev = brightness;

        // 计数
        count += 1;
        /*向串口打印 */
        Serial.print("Humidity:"); //向串口打印 Humidity:
        Serial.print(RH);          //向串口打印湿度数据
        Serial.print("%");
        Serial.print("  Temperature:");
        Serial.print(T); //向串口打印温度数据
        Serial.println("C");
        Serial.print("Illumination:");
        Serial.println(illumination);
        Serial.print("MotionAlarmState:");
        Serial.println(motion);
        Serial.print("CollideStatus");
        Serial.println(collide);
        Serial.print("OverTiltError");
        Serial.println(tilt);
        Serial.print("AlarmState");
        Serial.println(alarmState);

        //上报和归零
        if (millis() - lastMs >= 5000)
        {
            lastMs = millis();
            mqttCheckConnect();
            /* 上报 */
            mqttIntervalPost();
            //上报数据清零
            count = 0;
            RH_sum = 0;
            T_sum = 0;
            illumination_sum = 0;
            motion_sum = 0;
            collide_sum = 0;
            tilt_sum = 0;
            alarmState_sum = 0;
        }
        client.loop();
        delay(1000); //延时1000毫秒
    }
}